
#ifndef GMACHINE_HEADER_GSTACKER_H
#define GMACHINE_HEADER_GSTACKER_H

#include "GObjects.h"
#include "GProfile.h"
#include "GRegister.h"

namespace GMachine
{
	///////////////////////////////////////////////////////////////
	// Stacker
	///////////////////////////////////////////////////////////////
	class GStacker
	{
	private:
		typedef U8	MarkerBlock;
		struct		JumperBlock { void * base; void * head; };
/*
		struct		JumperBlock {
		public:
			JumperBlock(U8 name, void * base, void * head):addr(base), info((head - base) << 8 + name) {}
			void *	GetBase() const { return addr; }
			void *	GetHead() const	{ return (info >> 8); }
			U8		GetName() const { return (U8)(info & 0xFF) }
		private:
			void *	addr;
			U32		info;
		};
*/
	private:
		template <int> struct StackSelector;

		template<> struct StackSelector<1>
		{
			typedef U8*&  selected;
			static inline selected Select(GStacker* stacker)
			{
				return stacker->B1StackHead;
			}
		};

		template<> struct StackSelector<2>
		{
			typedef U16*& selected;
			static inline selected Select(GStacker* stacker)
			{
				return stacker->B2StackHead;
			}
		};

		template<> struct StackSelector<4>
		{
			typedef U32*& selected;
			static inline selected Select(GStacker* stacker)
			{
				return stacker->B4StackHead;
			}
		};

		template<> struct StackSelector<8>
		{
			typedef U64*& selected;
			static inline selected Select(GStacker* stacker)
			{
				return stacker->B8StackHead;
			}
		};

	private:
		void Write(U8*& stack, U8* data, unsigned int count)
		{
			if (stack - B1StackBase < sizeof(U8) * count)
			{
				Mark(Cellname::B1JBCELL);
			}

		}

		void Clear(U8*& stack, unsigned int count)
		{
		}

		void Write(U16*& stack, U16* data, unsigned int count)
		{
		}

		void Clear(U16*& stack, unsigned int count)
		{
		}

		void Write(U32*& stack, U32* data, unsigned int count)
		{
		}

		void Clear(U32*& stack, unsigned int count)
		{
		}

		void Write(U64*& stack, U64* data, unsigned int count)
		{
		}

		void Clear(U64*& stack, unsigned int count)
		{
		}


	private:
		void Prepare()
		{
			if (MBStackHead == MBStackBase)
			{
				JumperBlock mbjb = { MBStackBase, MBStackHead };
				MBStackBase = new MarkerBlock[4096];		//min size == 3
				MBStackHead = MBStackBase + sizeof(MarkerBlock[4096]);
				if (JBStackHead == JBStackBase)
				{
					JumperBlock jbjb = { JBStackBase, JBStackHead };
					JBStackBase = new JumperBlock[4096];	//min size == 3
					JBStackHead = JBStackBase + sizeof(JumperBlock[4096])
					*(--MBStackHead) = Cellname::JBJBCELL;
					*(--JBStackHead) = jbjb;
				}
				*(--MBStackHead) = Cellname::MBJBCELL;
				*(--JBStackHead) = mbjb;
			}
			return;
		}

	private:
		template <U8 cellname> struct Marker {
			static inline void Mark(GStacker* stacker) {
				stacker->Prepare();
				*(--stacker->MBStackHead) = marker;
			}
		};

		template<> struct Marker<Cellname::B1JBCELL> {
			static inline void Mark(GStacker* stacker) {
				stacker->Prepare();
				JumperBlock jumperblock = { stacker->B1StackBase, stacker->B1StackHead };
				stacker->B1StackBase = new U8[4096];
				stacker->B1StackHead = stacker->B1StackBase + sizeof(U8[4096]);
				*(--stacker->MBStackHead) = Cellname::B1JBCELL;
				*(--stacker->JBStackHead) = jumperblock;
			}
		};

		template<> struct Marker<Cellname::B2JBCELL> {
			static inline void Mark(GStacker* stacker) {
				stacker->Prepare();
				JumperBlock jumperblock = { stacker->B2StackBase, stacker->B2StackHead };
				stacker->B2StackBase = new U16[4096];
				stacker->B2StackHead = stacker->B2StackBase + sizeof(U16[4096]);
				*(--stacker->MBStackHead) = Cellname::B2JBCELL;
				*(--stacker->JBStackHead) = jumperblock;
			}
		};

		template<> struct Marker<Cellname::B4JBCELL> {
			static inline void Mark(GStacker* stacker) {
				stacker->Prepare();
				JumperBlock jumperblock = { stacker->B4StackBase, stacker->B4StackHead };
				stacker->B4StackBase = new U32[4096];
				stacker->B4StackHead = stacker->B1StackBase + sizeof(U32[4096]);
				*(--stacker->MBStackHead) = Cellname::B4JBCELL;
				*(--stacker->JBStackHead) = jumperblock;
			}
		};

		template<> struct Marker<Cellname::B8JBCELL> {
			static inline void Mark(GStacker* stacker) {
				stacker->Prepare();
				JumperBlock jumperblock = { stacker->B8StackBase, stacker->B8StackHead };
				stacker->B8StackBase = new U64[4096];
				stacker->B8StackHead = stacker->B2StackBase + sizeof(U64[4096]);
				*(--stacker->MBStackHead) = Cellname::B8JBCELL;
				*(--stacker->JBStackHead) = jumperblock;
			}
		};

	private:
		void Unmark()
		{
			for (;;) switch (*stackCursor.MBStackHead++) 
			{
			case Cellname::MBJBCELL: {
					delete[] MBStackBase;
					MBStackBase = JBStackHead->base;
					MBStackHead = JBStackHead->head;
					++JBStackHead;
				}
				continue;
			case Cellname::JBJBCELL: {
					JumperBlock * j = JBStackBase;
					JBStackBase = JBStackHead->base;
					JBStackHead = JBStackHead->head;
					delete[] j;
				};
				continue;
			case Cellname::B1JBCELL: {
					delete[] B1StackBase;
					B1StackBase = JBStackHead->base;
					B1StackHead = JBStackHead->head;
					++JBStackHead;
				};
				continue;
			case Cellname::B2JBCELL: {
					delete[] B2StackBase;
					B2StackBase = JBStackHead->base;
					B2StackHead = JBStackHead->head;
					++JBStackHead;
				};
				continue;
			case Cellname::B4JBCELL: {
					delete[] B4StackBase;
					B4StackBase = JBStackHead->base;
					B4StackHead = JBStackHead->head;
					++JBStackHead;
				};
				continue;
			case Cellname::B8JBCELL: {
					delete[] B8StackBase;
					B8StackBase = JBStackHead->base;
					B8StackHead = JBStackHead->head;
					++JBStackHead;
				};
				continue;
			default:
				return;
			}
		}
	public:
		CellObject Pop()
		{
			switch (*MBStackHead)
			{
			case Cellname::GCELL:
				{
					StackSelector<TraitsOfCell<Cellname::GCELL>::size>::selected stack = StackSelector<TraitsOfCell<Cellname::GCELL>::size>::Select(this);
					IntegerObject * g = stack;	//X
					Unmark(); Clear(stack, 1);
					return CellObject(Cellname::GCELL, g);
				}
			case Cellname::RGCELL:
				{
					StackSelector<TraitsOfCell<Cellname::RCELL>::size>::selected stack = StackSelector<TraitsOfCell<Cellname::RCELL>::size>::Select(this);
					IntegerObject * g = reg.G[*stack];
					Unmark(); Clear(stack, 1);
					return CellObject(Cellname::GCELL, g);
				}
			case Cellname::VOIDCELL:
				return CellObject(Cellname::VOIDCELL, 0);
			}
		}

		void Push(const IntegerObject& g)
		{
			Marker<Cellname::GCELL>::Mark(this); Write(StackSelector<TraitsOfCell<Cellname::GCELL>::size>::Select(this), &g, 1);
			return;
		}

		IntegerObject * PopInteger(GRegister& reg, GStackCursor & cursor)
		{
			switch (*(cursor.MBStack))
			{
			case Cellname::GCELL:
				{
					StackSelector<TraitsOfCell<Cellname::GCELL>::size>::selected stack = StackSelector<TraitsOfCell<Cellname::GCELL>::size>::Select(this);
					IntegerObject * g = stack;
					Unmark(); Clear(stack, 1);
					return g;
				}
			case Cellname::RGCELL:
				{
					StackSelector<TraitsOfCell<Cellname::RCELL>::size>::selected stack = StackSelector<TraitsOfCell<Cellname::RCELL>::size>::Select(this);
					IntegerObject * g = reg.G[*stack];
					Unmark(); Clear(stack, 1);
					return g;
				}
			}
			return reg.GetInvalidGReg();
		}

		void Push(const FloatingObject& f)
		{
			if (EnsureAvailableStackSize(TraitsOfCell<Cellname::FCELL>::size, TraitsOfCell<Cellname::TYPECELL>::size))
			{
				cellCursor.dataCell -= TraitsOfCell<Cellname::FCELL>::size;
				*(TraitsOfCell<Cellname::FCELL>::type*)(cellCursor.dataCell) = g;
				cellCursor.typeCell -= TraitsOfCell<Cellname::TYPECELL>::size;
				*(TraitsOfCell<Cellname::TYPECELL>::type*)(cellCursor.typeCell) = Cellname::GCELL;
			}
			return;
		}

		void Push(const Regname::TRG& r)
		{
			if (EnsureAvailableStackSize(TraitsOfCell<Cellname::FCELL>::size, TraitsOfCell<Cellname::TYPECELL>::size))
			{
				cellCursor.dataCell -= TraitsOfCell<Cellname::FCELL>::size;
				*(TraitsOfCell<Cellname::FCELL>::type*)(cellCursor.dataCell) = g;
				cellCursor.typeCell -= TraitsOfCell<Cellname::TYPECELL>::size;
				*(TraitsOfCell<Cellname::TYPECELL>::type*)(cellCursor.typeCell) = Cellname::GCELL;
			}

			const int requiredsize = TraitsOfCell<Cellname::RCELL>::size + TraitsOfCell<Cellname::RCELL>::size;
			if (framebase - stackbase >= requiredsize)
			{
				framebase -= requiredsize;
				*(framebase) = Cellname::RGCELL;
				*(framebase + TraitsOfCell<Cellname::RCELL>::size) = r.regid;
				return;
			}
			//throw();
		}

		void Push(const Regname::TRF& r)
		{
			const int requiredsize = TraitsOfCell<Cellname::RCELL>::size + TraitsOfCell<Cellname::RCELL>::size;
			if (framebase - stackbase >= requiredsize)
			{
				framebase -= requiredsize;
				*(framebase) = Cellname::RGCELL;
				*(framebase + TraitsOfCell<Cellname::RCELL>::size) = r.regid;
				return;
			}
			//throw();
		}

		void Push(const Regname::TRS& r)
		{
			const int requiredsize = TraitsOfCell<Cellname::RCELL>::size + TraitsOfCell<Cellname::RCELL>::size;
			if (framebase - stackbase >= requiredsize)
			{
				framebase -= requiredsize;
				*(framebase) = Cellname::RGCELL;
				*(framebase + TraitsOfCell<Cellname::RCELL>::size) = r.regid;
				return;
			}
			//throw();
		}

		void Push(const Regname::TRH& r)
		{
			const int requiredsize = TraitsOfCell<Cellname::RCELL>::size + TraitsOfCell<Cellname::RCELL>::size;
			if (framebase - stackbase >= requiredsize)
			{
				framebase -= requiredsize;
				*(framebase) = Cellname::RGCELL;
				*(framebase + TraitsOfCell<Cellname::RCELL>::size) = r.regid;
				return;
			}
			//throw();
		}
	private:
		U8 *			B1StackBase;
		U8 *			B1StackHead;
		U16 *			B2StackBase;
		U16 *			B2StackHead;
		U32 *			B4StackBase;
		U32 *			B4StackHead;
		U64	*			B8StackBase;
		U64	*			B8StackHead;
		MarkerBlock *	MBStackBase;
		MarkerBlock *	MBStackHead;
		JumperBlock *	JBStackBase;
		JumperBlock *	JBStackHead;

		MarkerBlock *	RTStackMark;
	};




	/////////////////////////////////////////////////////////////////////
	// Function: PickInteger, PickFloating, PickString, PickHandle
	/////////////////////////////////////////////////////////////////////
	// PickInteger:
	inline IntegerObject* PickInteger(GRegister& reg, GStackCursor& stack)
	{
		PERFCOUNTER("Pick Integer");
		switch (*frame)
		{
		case Cellname::RCELL:
			{
				IntegerObject* obj = reg.G + frame[1];
				frame += TraitsOfCell<Cellname::RCELL>::size + TraitsOfCell<Cellname::RCELL>::size;
				return obj;
			}
			break;
		case Cellname::GCELL:
			{
				IntegerObject* obj = (IntegerObject*)(frame + TraitsOfCell<Cellname::RCELL>::size);
				frame += TraitsOfCell<Cellname::RCELL>::size + TraitsOfCell<Cellname::GCELL>::size;
				return obj;
			}
			break;
		}
		return reg.GetInvalidGReg();
	}

	// PickFloating:
	inline FloatingObject* PickFloating(GRegister& reg, U8 *& frame)
	{
		switch (*frame)
		{
		case Cellname::RCELL:
			{
				FloatingObject* obj = reg.F + frame[1];
				frame += TraitsOfCell<Cellname::RCELL>::size + TraitsOfCell<Cellname::RCELL>::size;
				return obj;
			}
			break;
		case Cellname::FCELL:
			{
				FloatingObject* obj = (FloatingObject*)(frame + TraitsOfCell<Cellname::RCELL>::size);
				frame += TraitsOfCell<Cellname::RCELL>::size + TraitsOfCell<Cellname::FCELL>::size;
				return obj;
			}
			break;
		}
		return reg.GetInvalidFReg();
	}

	// PickString:
	inline StringObject* PickString(GRegister& reg, U8 *& frame)
	{
		if (frame[0] == Cellname::RCELL) 
		{
			StringObject* obj = reg.S + frame[1];
			frame += TraitsOfCell<Cellname::RCELL>::size + TraitsOfCell<Cellname::RCELL>::size;
			return obj;
		} 
		return reg.GetInvalidSReg();
	}

	// PickHandle:
	inline HandleObject* PickHandle(GRegister& reg, U8 *& frame)
	{
		if (frame[0] == Cellname::RCELL)
		{
			HandleObject* obj = reg.H + frame[1];
			frame += TraitsOfCell<Cellname::RCELL>::size + TraitsOfCell<Cellname::RCELL>::size;
			return obj;
		} 
		return reg.GetInvalidHReg();
	}
	
}

#endif	//ifndef GMACHINE_HEADER_GSTACKER_H


/*
		//void PushVal(const GRegister& reg, const Regname::TRG& gr)
		//{
		//	const int framesize = TraitsOfCell<Cellname::GCELL>::size + sizeof(U8);
		//	if (framebase - stackbase >= framesize)
		//	{
		//		framebase -= framesize;				
		//		*(IntegerObject*)(framebase + sizeof(U8)) = *(IntegerObject*)(reg.GetRegReference(gr.regid));
		//		*framebase = Cellname::GCELL;
		//		return;
		//	}
		//	//throw();
		//}

		//void PushVal(const GRegister& reg, const Regname::TRF& fr)
		//{
		//	const int framesize = TraitsOfCell<Cellname::FCELL>::size + sizeof(U8);
		//	if (framebase - stackbase >= framesize)
		//	{
		//		framebase -= framesize;				
		//		*(FloatingObject*)(framebase + sizeof(U8)) = *(FloatingObject*)(reg.GetRegReference(fr.regid));
		//		*framebase = Cellname::FCELL;
		//		return;
		//	}
		//	//throw();
		//}
*/
